import { _decorator, Node, Prefab, instantiate, randomRangeInt, randomRange,  Vec3, ITriggerEvent,
    BoxCollider, Collider, Input, input, Label
 } from 'cc';
import BaseView from '../../../../../../extensions/app/assets/base/BaseView';
import { IMiniViewNames } from '../../../../../app-builtin/app-admin/executor';
import { ecs, NodeComponent, filter } from 'db://pkg/@gamex/cc-ecs';
import { ShootSystem } from './expansion/ecs/system/ShootSystem';
import { MyEntity } from './expansion/ecs/entity/MyEntity';
import { PlayerComponent } from './expansion/ecs/component/PlayerComponent';
import { CollideComponent } from './expansion/ecs/component/CollideComponent';
import { GameController } from 'db://assets/app-builtin/app-controller/GameController';
import { DamageComponent } from './expansion/ecs/component/DamageComponent';
import { BulletComponent } from './expansion/ecs/component/BulletComponent';
import { app } from 'db://assets/app/app';
import { DataSingleton } from './expansion/ecs/singleton/DataSingleton';
import { ColliderSystem } from './expansion/ecs/system/ColliderSystem';
import { DestroySystem } from './expansion/ecs/system/DestroySystem';
import { EnemySystem } from './expansion/ecs/system/EnemySystem';
import { EnemyComponent } from './expansion/ecs/component/EnemyComponent';
import { DestroyComponent } from './expansion/ecs/component/DestroyComponent';
import { PoolManager } from './PoolManager';
import { InputSystem } from './expansion/ecs/system/InputSystem';
import { InputSingleton } from './expansion/ecs/singleton/InputSingleton';
import { AutoMoveComponent } from './expansion/ecs/component/AutoMoveComponent';
import { AutoMoveSystem } from './expansion/ecs/system/AutoMoveSystem';
import { RockerEventType, Rocker, RockerType } from 'db://pkg/@gamex/cc-ctrl-rocker';
import { gameRange, playerBulletType } from './Common';
import { PropComponent } from './expansion/ecs/component/PropComponent';
import { ItemSystem } from './expansion/ecs/system/ItemSystem';
const { ccclass, property } = _decorator;

enum Group {
    Player = 1 << 0,
    Bullet = 1 << 1,
    Enemy = 1 << 2,
    EnemyBullet = 1 << 3,
    Prop = 1 << 4,
}

enum Mask {
    Player = Group.Enemy | Group.EnemyBullet | Group.Prop,
    Bullet = Group.Enemy,
    Enemy = Group.Bullet | Group.Player,
    EnemyBullet = Group.Player,
    Prop = Group.Player,
}

@ccclass('PageWorld')
export class PageWorld extends BaseView.BindController(GameController)  {
    // 子界面列表，数组顺序为子界面排列顺序
    protected miniViews: IMiniViewNames = [];
    
    @property(Prefab)
    private player: Prefab = null;

    @property([Prefab])
    private bulletPrefabs: Prefab[] = [];
    @property([Prefab])
    private enemyPrefabs: Prefab[] = [];
    @property([Prefab])
    private items: Prefab[] = [];
    @property(Prefab)
    private enemyExplode: Prefab = null;

    @property(Node)
    private rocker: Node = null;
    private rockerComp: Rocker = null;
    @property(Label)
    private lbl_score: Label = null;

    private running = true;

    // 初始化的相关逻辑写在这
    onLoad() {
        PoolManager.instance.clear();
        
        ecs.addSystem(InputSystem);
        ecs.addSystem(EnemySystem);
        ecs.addSystem(ShootSystem);
        ecs.addSystem(AutoMoveSystem);
        ecs.addSystem(ColliderSystem);
        ecs.addSystem(DestroySystem);
        ecs.addSystem(ItemSystem);
        ecs.addSingleton(DataSingleton);


        this.controller.on(GameController.Event.Shoot, this.onShoot, this);
        this.controller.on(GameController.Event.EnemyShoot, this.onEnemyShoot, this);
        this.controller.on(GameController.Event.Enemy, this.onEnemy, this);
        this.controller.on(GameController.Event.EnemyExplode, this.onPlayEnemyExplode, this);
        this.controller.on(GameController.Event.Prop, this.onProp, this);
        this.controller.on(GameController.Event.CollectEnemy, this.onCollectEnemy, this);
        this.controller.on(GameController.Event.CollectBullet, this.onCollectBullet, this);
        this.controller.on(GameController.Event.UpdateScore, this.onUpdateScore, this);

        this.initPlayer();
    }

    // 界面打开时的相关逻辑写在这(onShow可被多次调用-它与onHide不成对)
    onShow(params: any) { this.showMiniViews({ views: this.miniViews }); }

    // 界面关闭时的相关逻辑写在这(已经关闭的界面不会触发onHide)
    onHide(result: undefined) {
        ecs.clear();
        this.controller.targetOff(this);
        // app.manager.ui.show<PageWorld>({name: 'PageWorld', onHide:(result) => { 接收到return的数据，并且有类型提示 }})
        return result;
    }

    protected onFocus() {
        this.running = true;
    }
    
    protected onLostFocus() {
        this.running = false;
    }

    update(dt: number) {
        if (!this.running) return;
        
        if (ecs.getSingleton(DataSingleton).over === true) {
            this.rockerComp.node.off(RockerEventType.Change, this.onRockerChange, this);
            this.rockerComp.node.off(RockerEventType.Stop, this.onRockerStop, this);
            input.off(Input.EventType.KEY_DOWN, InputSystem.onKeyDown, this);
            input.off(Input.EventType.KEY_UP, InputSystem.onKeyUp, this);
        } else {
            ecs.execute(dt);
            if (ecs.getSingleton(DataSingleton).over === true) {
                app.manager.sound.playMusic({ name: 'music/over' });
                this.scheduleOnce(() => {
                    app.manager.ui.show({
                        name: 'PopOver',
                        data: {
                            score: ecs.getSingleton(DataSingleton).score, 
                        }
                    })
                }, 0.3)
            }
        }
    }

    private initPlayer() {
        const player = instantiate(this.player);
        this.node.addChild(player);

        const entity = ecs.createEntity(MyEntity, { node: player });
        ecs.getSingleton(DataSingleton).entityMap.set(player, entity)
    
        const playComp = entity.add(PlayerComponent);
        playComp.maxHp = 40;
        playComp.hp = playComp.maxHp;
        playComp.attack = 1;

        const node = entity.add(NodeComponent);
        node.setPosition(0, 0, 5);
        const damage = entity.add(DamageComponent);
        // 玩家撞击伤害
        damage.attack = 9999;

        const boxCollider = player.getComponent(BoxCollider);
        boxCollider.setGroup(Group.Player);
        boxCollider.setMask(Mask.Player);
        boxCollider.on('onTriggerEnter', this.onPlayerTriggerEnter, this);

        // 初始化后玩家后 注册摇杆事件
        const inputManager = ecs.addSingleton(InputSingleton);
        inputManager.toward = Vec3.ZERO;
        this.rockerComp = this.rocker.getComponent(Rocker);
        this.rockerComp.type = RockerType.Free;
        this.rockerComp.node.on(RockerEventType.Change, this.onRockerChange, this);
        this.rockerComp.node.on(RockerEventType.Stop, this.onRockerStop, this);
        input.on(Input.EventType.KEY_DOWN, InputSystem.onKeyDown, this);
        input.on(Input.EventType.KEY_UP, InputSystem.onKeyUp, this);
    }

    private onShoot(playerNode: NodeComponent, player: PlayerComponent) {
        const count = player.bulletType === playerBulletType.M ? 1 : player.bulletType === playerBulletType.H ? 2 : 3;
        const bulletIndex = player.bulletType === playerBulletType.M? 0 : player.bulletType === playerBulletType.H? 2 : 4;

        for (let index = 0; index < count; index++) {
            const bullet = PoolManager.instance.getNode(this.bulletPrefabs[bulletIndex]);
            
            if (!bullet.parent) {
                this.node.addChild(bullet);
            }
            if (player.bulletType === playerBulletType.M) {
                bullet.x = playerNode.x;
            } else if (player.bulletType === playerBulletType.H) {
                bullet.x = playerNode.x + (index === 0  ? -0.5 : 0.5);
            } else {
                bullet.x = playerNode.x + (index - 1) * 0.4;
            }
            bullet.y = playerNode.y;
            bullet.z = playerNode.z - 1;

            const entity = ecs.createEntity(MyEntity, { node: bullet });
            ecs.getSingleton(DataSingleton).entityMap.set(bullet, entity)

            const damage = entity.add(DamageComponent);
            damage.attack = player.attack;

            const node = entity.add(NodeComponent);
            node.setPosition(bullet.x, bullet.y, bullet.z);

            const boxCollider = bullet.getComponent(Collider);
            boxCollider.setGroup(Group.Bullet);
            boxCollider.setMask(Mask.Bullet);
            boxCollider.on('onTriggerEnter', this.onBulletTriggerEnter, this);

            const move = entity.add(AutoMoveComponent);
            entity.node.forward = player.bulletType === playerBulletType.S ? new Vec3((index - 1) * 0.4, 0 , -1).normalize() : player.entity.node.forward;
            move.speed = 8;
            move.maxDistance = 10;
            move.toward = entity.node.forward;

            // 子弹组件
            const bulletComp = entity.add(BulletComponent);
            bulletComp.owner = player.entity;
        }

        // app.manager.sound.playEffect({ name: 'effect/shoot', volume: 0.6, interval: 0.1 });
    }

    private onCollectBullet(node: Node) {
        ecs.getSingleton(DataSingleton).entityMap.delete(node);
        PoolManager.instance.putNode(node);
        
    }

    /** 
     * 创建敌人 
     * 4种类型： 
     * 1. 直线 3 个 敌型类型1 不发射子弹
     * 2. 扇形 5 个 敌型类型2 发射子弹
     */
    private onEnemy(combinationType: number = 0) {
        if (combinationType === 0) {
            this.createEnemy(randomRangeInt(0, 2), randomRange(gameRange.minX+0.5, gameRange.maxX-0.5), gameRange.minZ);
        } else {
            for (let index = 0; index < 5; index++) {
                this.createEnemy(1, (index - 2) * 2, gameRange.minZ - Math.abs(index - 2) * 0.8);
            } 
        }
        
    }

    createEnemy(typeIndex: number, x: number, z: number) {
        const enemy = PoolManager.instance.getNode(this.enemyPrefabs[typeIndex]);
        if (!enemy.parent) {
            this.node.addChild(enemy);
        }
        enemy.x = x;
        enemy.y = 0;
        enemy.z = z;

        const entity = ecs.createEntity(MyEntity, { node: enemy });
        ecs.getSingleton(DataSingleton).entityMap.set(enemy, entity)

        const node = entity.add(NodeComponent);
        node.setPosition(enemy.x, 0, enemy.z);
        enemy.forward = new Vec3(0, 0, 1);

        const collider = entity.add(CollideComponent);
        collider.body.setGroup(Group.Enemy);
        collider.body.setMask(Mask.Enemy);
        collider.body.setRect(node.boundingBox);

        const boxCollider = enemy.getComponent(BoxCollider);
        boxCollider.setGroup(Group.Enemy);
        boxCollider.setMask(Mask.Enemy);
        boxCollider.on('onTriggerEnter', this.onEnemyTriggerEnter, this);
        
        const move = entity.add(AutoMoveComponent);
        move.speed = 2;
        move.toward = enemy.forward;
        // 敌人组件
        const enemyComp = entity.add(EnemyComponent);
        if (typeIndex === 1) {
            enemyComp.maxHp = 1;
            enemyComp.hp = enemyComp.maxHp;
            enemyComp.canShoot = false;
            enemyComp.score = 10;
            move.speed = 3;
        } else {
            enemyComp.maxHp = 2;
            enemyComp.hp = enemyComp.maxHp;
            enemyComp.canShoot = true;
            enemyComp.attack = 10;
            enemyComp.interval = 2;
            enemyComp.score = 15;
        }
        const damage = entity.add(DamageComponent);
        // 敌人撞击伤害
        damage.attack = enemyComp.attack;
    }

    private onEnemyShoot(enemyComp: EnemyComponent) {
        if (!enemyComp.canShoot) return;
        const bullet = PoolManager.instance.getNode(this.bulletPrefabs[3]);
        if (!bullet.parent) {
            this.node.addChild(bullet); 
        }
        bullet.x = enemyComp.entity.node.x;
        bullet.y = enemyComp.entity.node.y;
        bullet.z = enemyComp.entity.node.z + 1;

        const entity = ecs.createEntity(MyEntity, { node: bullet });
        ecs.getSingleton(DataSingleton).entityMap.set(bullet, entity);
        const damage = entity.add(DamageComponent);
        damage.attack = enemyComp.attack;

        const node = entity.add(NodeComponent);
        node.setPosition(bullet.x, bullet.y, bullet.z);

        const boxCollider = bullet.getComponent(Collider);
        boxCollider.setGroup(Group.EnemyBullet);
        boxCollider.setMask(Mask.EnemyBullet);
        boxCollider.on('onTriggerEnter', this.onBulletTriggerEnter, this);
            

        const move = entity.add(AutoMoveComponent);
        move.speed = 4;
        
        const playerEntity = ecs.find(filter.all(PlayerComponent).exclude(DestroyComponent));
        // 子弹朝向玩家
        if (playerEntity) {
            const playerPos = playerEntity.get(NodeComponent).position;
            if (playerPos.z <= bullet.z) {
                bullet.forward = new Vec3(0, 0, 1);
            } else {
                bullet.forward = new Vec3(playerPos.x, playerPos.y, playerPos.z).subtract(bullet.position).normalize();
            }
        } else {
            bullet.forward = new Vec3(0, 0, 1);
        }
        move.toward = bullet.forward;
        // 子弹组件
        const bulletComp = entity.add(BulletComponent);
        bulletComp.owner = enemyComp.entity;
    }

    private onCollectEnemy(node: Node) {
        ecs.getSingleton(DataSingleton).entityMap.delete(node);
        PoolManager.instance.putNode(node);
    }

    onPlayerTriggerEnter(event: ITriggerEvent) {
        const otherEntity = ecs.getSingleton(DataSingleton).entityMap.get(event.otherCollider.node);
        if (otherEntity.has(PropComponent)) {
           const prop = otherEntity.get(PropComponent);
           const playerEnity = ecs.getSingleton(DataSingleton).entityMap.get(event.selfCollider.node);
           playerEnity.getComponent(PlayerComponent).bulletType = prop.bulletType;
           app.manager.sound.playEffect({ name: 'effect/eat' });
        }
        if (!otherEntity.has(DamageComponent)) return;
        const playerEnity = ecs.getSingleton(DataSingleton).entityMap.get(event.selfCollider.node)
        playerEnity.getComponent(PlayerComponent).hp -= otherEntity.get(DamageComponent).attack;
        if (playerEnity.getComponent(PlayerComponent).hp <= 0) {
            playerEnity.add(DestroyComponent);
        }
    }

    onEnemyTriggerEnter(event: ITriggerEvent) {
        const otherEntity = ecs.getSingleton(DataSingleton).entityMap.get(event.otherCollider.node);
        if (!otherEntity.has(DamageComponent)) return;
        const enemyEnity = ecs.getSingleton(DataSingleton).entityMap.get(event.selfCollider.node);
        enemyEnity.getComponent(EnemyComponent).hp -= otherEntity.get(DamageComponent).attack;
        app.manager.sound.playEffect({ name: 'effect/hit', interval: 0.1 });
        if (enemyEnity.getComponent(EnemyComponent).hp <= 0) {
            if (!enemyEnity.has(DestroyComponent)) {
                const destroyComp = enemyEnity.add(DestroyComponent)
                destroyComp.killed = true;
            }

        }
    }
    onBulletTriggerEnter(event: ITriggerEvent) {
       const entity = ecs.getSingleton(DataSingleton).entityMap.get(event.selfCollider.node)
       if (!entity.has(DestroyComponent)) {
            entity.addComponent(DestroyComponent);
       }
    }

    onRockerChange(vec3: Vec3, ratio: number) {
        vec3.normalize();
        ecs.getSingleton(InputSingleton).toward = new Vec3(vec3.x, 0, -vec3.y);
    }
    onRockerStop() {
        ecs.getSingleton(InputSingleton).toward = Vec3.ZERO;
    }
    onProp(node: NodeComponent) {
        const index = randomRangeInt(0, this.items.length);
        
        const item = this.items[index];
        const prop = PoolManager.instance.getNode(item);
        if (!prop.parent) {
            this.node.addChild(prop);
        }
        prop.setPosition(0, 0, gameRange.minZ);
        const entity = ecs.createEntity(MyEntity, { node: prop });
        ecs.getSingleton(DataSingleton).entityMap.set(prop, entity);
        const nodeComp = entity.add(NodeComponent);
        nodeComp.setPosition(prop.x, prop.y, prop.z);
        const move = entity.add(AutoMoveComponent);
        move.speed = 2;
        move.toward = new Vec3(randomRangeInt(0,2) === 0 ? -1 : 1, 0, 1).normalize();
        const boxCollider = prop.getComponent(BoxCollider);
        boxCollider.setGroup(Group.Prop);
        boxCollider.setMask(Mask.Prop);
        boxCollider.on('onTriggerEnter', this.onBulletTriggerEnter, this);
        entity.add(PropComponent).bulletType = index;
    }

    onPlayEnemyExplode(node: Node) {
        const explode = PoolManager.instance.getNode(this.enemyExplode);
        if (!explode.parent) {
            this.node.addChild(explode);
        }
        explode.setPosition(node.x, node.y, node.z);
    }

    onUpdateScore(score: number) {
        this.lbl_score.string = score.toString();
    }

}